#include "ClassGenerator.h"
#include "MetaGenerator.h"
#include "FieldGenerator.h"
#include "PropertyGenerator.h"
#include "FunctionGenerator.h"

#include "HeadFile.h"

#include <cassert>

CClassGenerator::CClassGenerator(CHeadFile *InContext)
{
    Context = InContext;
    ClassInfo = std::make_shared<CClassInfo>();
}

void CClassGenerator::Generate()
{
    //skip RCLASS
    {
        auto Token = Context->GetCurrentToken();
        assert(Token->Type == ETokenType::RCLASS 
            || Token->Type == ETokenType::RSTRUCT
            || Token->Type == ETokenType::RINTERFACE
            );
        
        ClassInfo->RCLASSLineNumber= Token->FilePosition.Line;


        if(Token->Type == ETokenType::RSTRUCT)
        {
            ClassInfo->ClassType = EClassType::Struct;
        }
        else if(Token->Type == ETokenType::RINTERFACE)
        {
            ClassInfo->ClassType = EClassType::Interface;
        }
        else
        {
            ClassInfo->ClassType = EClassType::Class;
        }

        Context->GotoNextToken();
    }

    //get meta
    {
        auto Meta = CMetaGenerator(Context);
        Meta.Generate();
        ClassInfo->Meta = Meta.Result;
    }

    //skip class keyworld
    {
        auto Token = Context->GetCurrentToken();

        if(ClassInfo->IsStruct())
        {
            if(Token->Type != ETokenType::Struct)
            {
                Context->RaiseError(Token, U"expect 'struct'");
            }
        }
        else
        {
            if(Token->Type != ETokenType::Class)
            {
                Context->RaiseError(Token, U"expect 'class'");
            }
        }
        Context->GotoNextToken();
    }

    //get guoengine api
    {
        auto Token = Context->GetCurrentToken();
        if(Token->Type == ETokenType::EXPORT_API)
        {
            ClassInfo->ExportMacro = std::get<C32String>(Token->LiteralValue.value()).ToUtf8();
            Context->GotoNextToken();
        }
    }

    //get class name
    {
        auto Token = Context->GetCurrentToken();
        if(Token->Type == ETokenType::Identifier)
        {
            ClassInfo->Name = Token->ToString().ToUtf8();
            Context->GotoNextToken();
        }
        else
        {
            Context->RaiseError(Token, U"expect class name");
        }
    }

    //skip ':'
    if(ClassInfo->IsClass())
    {
        auto Token = Context->GetCurrentToken();
        if(Token->Type == ETokenType::Colon)
        {
            Context->GotoNextToken();
        }
        else
        {
            Context->RaiseError(Token, U"expect ':'");
        }
    }


    //read parent class
    if(!ClassInfo->IsStruct())
    {
        //try to find interfaces and base class
        while(true)
        {
            auto Token = Context->GetCurrentToken();
            if(Token->Type == ETokenType::Comma)
            {
                Context->GotoNextToken();
                Token=Context->GetCurrentToken();
            }
            
            if(Token->Type == ETokenType::Public
            || Token->Type == ETokenType::Protected
            || Token->Type == ETokenType::Private)
            {
                Context->GotoNextToken();
                Token=Context->GetCurrentToken();
            }

            if(Token->Type == ETokenType::Identifier)
            {
                auto BaseTypeName=Token->ToString().ToUtf8();

                if(BaseTypeName.at(0)=='I')
                {
                    ClassInfo->ImplementedInterfaces.push_back(BaseTypeName);
                }
                else
                {
                    ClassInfo->BaseClassName = BaseTypeName;
                }
                Context->GotoNextToken();
            }
            else
            {
                break;
            }
        }

        if(ClassInfo->IsClass() && ClassInfo->BaseClassName.empty())
        {
            Context->RaiseError(Context->GetCurrentToken(), U"expect base class name");
        }

    }

    //skip all tokens before '{'
    {
        auto Token = Context->GetCurrentToken();
        while(Token->Type != ETokenType::LeftBrace)
        {
            Context->GotoNextToken();
            Token = Context->GetCurrentToken();
        }
    }

    //skip '{'
    std::shared_ptr<CToken> LeftBraceToken;
    {
        auto Token = Context->GetCurrentToken();
        if(Token->Type == ETokenType::LeftBrace)
        {
            Context->GotoNextToken();
            LeftBraceToken=Token;
        }
        else
        {
            Context->RaiseError(Token, U"expect '{'");
        }
    }


    //read all member
    {
        auto Token = Context->GetCurrentToken();
        int BraceCount = 1;
        while(BraceCount>0)
        {
            switch(Token->Type)
            {
                case ETokenType::EndOfFile:
                    Context->RaiseError(LeftBraceToken, U"can't find '}' for '{' ,you may missed '}' or a ';' before '}' ");
                    break;

                case ETokenType::ROBJECT:
                    ClassInfo->ROBJECTLineNumber=Token->FilePosition.Line;
                    Context->GotoNextToken();
                    break;
                case ETokenType::LeftBrace:
                    BraceCount++;
                    Context->GotoNextToken();
                    break;
                case ETokenType::RightBrace:
                    BraceCount--;
                    Context->GotoNextToken();  // skip '}'
                    break;

                case ETokenType::RFIELD:
                {
                    auto PropertyGenerator = CFieldGenerator(Context);
                    PropertyGenerator.Generate();
                    ClassInfo->Fields.push_back(PropertyGenerator.Result);
                    break;
                }
                case ETokenType::RPROPERTY:
                {
                    auto PropertyGenerator = CPropertyGenerator(Context);
                    PropertyGenerator.Generate();
                    ClassInfo->Properties.push_back(PropertyGenerator.Result);
                    break;
                }

                case ETokenType::RFUNCTION:
                {
                    auto FunctionGenerator = CFunctionGenerator(Context);
                    FunctionGenerator.Generate();
                    ClassInfo->Functions.push_back(FunctionGenerator.Result);
                    break;
                }

                default:
                    Context->GotoNextToken();
            }

            Token = Context->GetCurrentToken();
        }
    }

    if(ClassInfo->ROBJECTLineNumber==-1)
    {
        Context->RaiseFileError(u8"must have ROBJECT macro in class/struct");
    }

    //skip '}'
    // {
    //     auto Token = Context->GetCurrentToken();
    //     if(Token->Type == ETokenType::RightBrace)
    //     {
    //         Context->GotoNextToken();
    //     }
    //     else
    //     {
    //         Context->RaiseError(Token, U"expect '}' at end of class");
    //     }
    // }
}
